﻿using FaissOperation.Interface;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;

namespace FaissOperation.LiteVectorCollection
{
    public static class DistanceProvider
    {
        public static float Distance(ReadOnlySpan<float> v1, ReadOnlySpan<float> v2, MetricType metric)
        {
            return metric switch
            {
                MetricType.L2 => L2DistanceSimd(v1, v2),
                MetricType.InnerProduct => -DotSimd(v1, v2), // 越大越相似 -> 取负当距离
                MetricType.Cosine => 1 - (DotSimd(v1, v2) / (Norm(v1) * Norm(v2) + 1e-10f)),
                _ => throw new NotImplementedException()
            };
        }

        public static void SimpleNormalize(Span<float> v)
        {
            float norm = Norm(v);
            if (norm > 1e-10f)
            {
                for (int i = 0; i < v.Length; i++)
                    v[i] /= norm;
            }
        }

        private static float L2DistanceSimd(ReadOnlySpan<float> v1, ReadOnlySpan<float> v2)
        {
            float sum = 0f;
            int i = 0;
            int simdLength = Vector<float>.Count;

            for (; i <= v1.Length - simdLength; i += simdLength)
            {
                var a = new Vector<float>(v1.Slice(i));
                var b = new Vector<float>(v2.Slice(i));
                var diff = a - b;
                sum += Vector.Dot(diff, diff);
            }

            for (; i < v1.Length; i++)
                sum += (v1[i] - v2[i]) * (v1[i] - v2[i]);

            return MathF.Sqrt(sum);
        }

        private static float DotSimd(ReadOnlySpan<float> v1, ReadOnlySpan<float> v2)
        {
            float dot = 0f;
            int i = 0;
            int simdLength = Vector<float>.Count;

            for (; i <= v1.Length - simdLength; i += simdLength)
            {
                var a = new Vector<float>(v1.Slice(i));
                var b = new Vector<float>(v2.Slice(i));
                dot += Vector.Dot(a, b);
            }

            for (; i < v1.Length; i++)
                dot += v1[i] * v2[i];

            return dot;
        }

        private static float Norm(ReadOnlySpan<float> v)
        {
            float sum = 0;
            foreach (var x in v) sum += x * x;
            return MathF.Sqrt(sum);
        }
    }

}
